/* 
 * 	This file is part of VPN Monitor Project 7..

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package monitor.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.Date;
import java.util.EnumMap;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

import monitor.gui.GuiControl.SettingsEnum;
import monitor.interfaces.TrafficStringPackage;
import monitor.interfaces.EnumTypes.Types;
import monitor.interfaces.observers.TrafficStringObserver;

@SuppressWarnings("serial")
class Log extends JPanel implements ColorObserver, TrafficStringObserver{
	private EnumMap<SettingsEnum,Style> styles = new EnumMap<SettingsEnum, Style>(SettingsEnum.class);
	private JTextPane textPane = null;
	private EnumMap<Types,Long> sessionData = null, logLevel = null;
	
	Log(GuiControl control, EnumMap<SettingsEnum, String> settings){
		control.addColorObserver(this);
		control.addTrafficStringObserver(this);
		StyleContext sc = new StyleContext();
		createStyles(sc, settings);
		createPanel(sc);
		sessionData = new EnumMap<Types, Long>(Types.class);
		logLevel = new EnumMap<Types, Long>(Types.class);
		for(Types t : Types.values()){
			sessionData.put(t, new Long(0));
			double n = new Double(settings.get(SettingsEnum.valueOf(("log_"+t.toString()).toUpperCase())));
			logLevel.put(t, (long) (n*1e6));
		}
	}
		
	@Override
	public void colorUpdated(SettingsEnum type, Color color) {
		if(type == SettingsEnum.COLOR_DOWNLOAD || type == SettingsEnum.COLOR_UPLOAD || type == SettingsEnum.COLOR_TOTAL)
			styles.get(type).addAttribute(StyleConstants.Foreground, color);
	}
	
	@Override
	public void stringsUpdated(TrafficStringPackage trafficStrings) {
		while(trafficStrings.getNumberOfTrafficStrings()>0)
			addTrafficEntry(trafficStrings.getType(), trafficStrings.getTrafficPackage().getTrafficString());
	}
	
	void addLogEntry(String entry) {
		StyledDocument log = textPane.getStyledDocument();
		try {
			if(entry!=null){
				addTimestamp(log);
				log.insertString(log.getLength(), entry,styles.get(SettingsEnum.COLOR_MESSAGE));
			}
			log.insertString(log.getLength(), "\n", styles.get(SettingsEnum.COLOR_MESSAGE));
		} catch (BadLocationException e) {
			e.printStackTrace();
		}
		textPane.setCaretPosition(log.getLength());
	}
	
	private void addTrafficEntry(Types type, String traffic){
		if(type!=null && traffic!=null){
			SettingsEnum se = SettingsEnum.valueOf(("color_"+type.toString()).toUpperCase());
			try{
				StyledDocument log = textPane.getStyledDocument();
				addTimestamp(log);
				log.insertString(log.getLength(), traffic+"\n", styles.get(se));
				textPane.setCaretPosition(log.getLength());
			} catch (BadLocationException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void createPanel(StyleContext sc){
		this.setLayout(new BorderLayout());
		textPane = new JTextPane(new DefaultStyledDocument(sc));
		textPane.setEditable(false);
		this.add(new JScrollPane(textPane), BorderLayout.CENTER);
		
		JButton saveToFile = new JButton("Save To File.");
		saveToFile.addActionListener(new ActionListener(){
			JFileChooser fc = null;
			@Override
			public void actionPerformed(ActionEvent ae) {
				if(fc==null)
					fc = new JFileChooser();
				int choise = fc.showSaveDialog(Log.this);
				if(choise == JFileChooser.APPROVE_OPTION){
					File file = fc.getSelectedFile();
					PrintWriter pw = null;
					try {
						if(!file.exists()){
							if(file.canWrite()){
								file.createNewFile();
								Log.this.addLogEntry("Created file: "+file.getName());
							} else
								Log.this.addLogEntry("Unable to created file "+file.getName()+", can't to file system.");
						}
						if(file.canWrite()){
							pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
							String[] lines = Log.this.textPane.getText().split("\n");
							for(String line : lines)
								pw.println(line.trim());
							Log.this.addLogEntry("Log printed to file "+file.getName());
						} else
							Log.this.addLogEntry("Can't write to file system.");
					} catch (IOException e) {
						e.printStackTrace();
					} finally {
						pw.close();
					}
				}
			}
			
		});
		add(saveToFile, BorderLayout.SOUTH);
	}
	
	private void createStyles(StyleContext sc, EnumMap<SettingsEnum, String> settings){
		SettingsEnum[] styles = SettingsEnum.styles();
		for(SettingsEnum style : styles){
			this.styles.put(style, sc.addStyle(style.toString(), null));
			Style s = this.styles.get(style);
			String[] color = settings.get(style).split("\\s+");			
			s.addAttribute(StyleConstants.Foreground, 
					new Color(Integer.parseInt(color[0]),
							Integer.parseInt(color[1]),
							Integer.parseInt(color[2]))
			);
			s.addAttribute(StyleConstants.FontSize, new Integer(12));
			s.addAttribute(StyleConstants.FontFamily, "monospaced");
			s.addAttribute(StyleConstants.Bold, new Boolean(false));
		}
	}
	
	private void addTimestamp(StyledDocument log) throws BadLocationException{
		Date date = new Date();
		DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT, this.getLocale());
		log.insertString(log.getLength(), df.format(date) + " ", styles.get(SettingsEnum.COLOR_TIMESTAMP));
	}
}
